package com.lyf.scm.plan.domain.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.lyf.scm.common.constants.CommonConstants;
import com.lyf.scm.common.enums.*;
import com.lyf.scm.plan.api.dto.StoreInfoDTO;
import com.lyf.scm.plan.domain.convertor.BigDataBaseConvertor;
import com.lyf.scm.plan.domain.remote.bigdata.dto.BigDataGeneralInfoDTO;
import com.lyf.scm.plan.domain.remote.bigdata.dto.BigDataGeneralQueryBaseDTO;
import com.lyf.scm.plan.domain.remote.bigdata.facade.CprfCoreFacade;
import com.lyf.scm.plan.domain.remote.item.dto.CategorySimpleDTO;
import com.lyf.scm.plan.domain.remote.item.dto.SkuInfoDTO;
import com.lyf.scm.plan.domain.remote.item.facade.ItemCoreFacade;
import com.lyf.scm.plan.domain.repository.DirectlyStoreRepository;
import com.lyf.scm.plan.domain.service.BigDataStoreHandlerService;
import com.lyf.scm.plan.domain.util.BigDataCacheUtil;
import com.lyf.scm.plan.insfrastructure.db.dataobject.bigdata.*;
import com.rome.arch.util.IdWorker;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

import static com.lyf.scm.plan.domain.util.BigDataCacheUtil.*;

/**
 * 门店处理
 *
 * @author wangchlei
 * @date 2020/10/23
 */
@Slf4j
@Service
public class BigDataStoreHandlerServiceImpl implements BigDataStoreHandlerService {
    @Resource
    private RedisCacheServiceImpl redisCacheServiceImpl;
    @Resource
    private DirectlyStoreRepository directlyStoreRepository;
    @Resource
    private ItemCoreFacade itemCoreFacade;
    @Resource
    private DirectlyStoreServiceImpl directlyStoreServiceImpl;
    @Resource
    private BigDataBaseConvertor bigDataBaseConvertor;
    /**
     * 远程调用
     */
    @Resource
    private CprfCoreFacade cprfCoreFacade;
    /**
     * 标识
     */
    private String opSign;

    /**
     * 处理
     *
     * @param
     * @return void
     **/
    @Async
    @Override
    public void handleApiDispatcher() {
        long startTime = System.currentTimeMillis();
        Map<String, StoreInfoDTO> storesInfo = redisCacheServiceImpl.getDirectStores();
        //初始化必要数据的缓存
        this.initCacheData();
        storesInfo.keySet().forEach(item -> this.handleStore(storesInfo.get(item)));
        long endTime = System.currentTimeMillis();
        this.writeOperateLog(endTime - startTime);
    }

    /**
     * 初始化必要的缓存信息
     *
     * @param
     * @return void
     **/
    private void initCacheData() {
        Date date = new Date();
        //注入周和日的处理缓存信息
        BigDataCacheUtil.injectDayCache(date);
        BigDataCacheUtil.injectWeekCache(date);
        //数据标识
        IdWorker idWorker = new IdWorker();
        opSign = "" + idWorker.getId();
    }

    /**
     * 处理每一个门店
     *
     * @param storeInfo
     * @return void
     **/
    private void handleStore(StoreInfoDTO storeInfo) {
        //直营预测计划——门店（天）
        List<DirectlyStoreInDO> directlyStoreInDOs = new ArrayList<>();
        //大数据信息
        long handleStartTime = System.currentTimeMillis();
        List<BigDataGeneralInfoDTO> bigDataGeneralInfoDTOs = null;
        try {
            //获取查询条件
            BigDataGeneralQueryBaseDTO bigDataGeneralQueryBaseDTO = this.getGeneralQueryBaseDTO(storeInfo.getCode(), BigDataCacheUtil.nowDate);
            //根据时间段，门店等信息  递归分页获取所有大数据信息
            bigDataGeneralInfoDTOs = this.getBigDataGeneralInfo(bigDataGeneralQueryBaseDTO);
            //合理默认两个表的状态是统一的，且其数据量数据结构是一致的（数据数量，sku,门店等是统一的）
            if (!CollectionUtils.isEmpty(bigDataGeneralInfoDTOs)) {
                this.getDayResult(storeInfo, bigDataGeneralInfoDTOs, directlyStoreInDOs);
            } else {
                return;
            }
        } catch (feign.RetryableException retryableException) {
            long handleEndTime = System.currentTimeMillis();
            retryableException.printStackTrace();
            this.writeErrorLog(storeInfo.getCode(), retryableException.getMessage(), bigDataGeneralInfoDTOs.size(),(handleEndTime - handleStartTime)+"ms");
            return;
        } catch (Exception e) {
            long handleEndTime = System.currentTimeMillis();
            e.printStackTrace();
            this.writeErrorLog(storeInfo.getCode(), ResponseMsg.FAIL_5004.getMsg(),  bigDataGeneralInfoDTOs.size(),(handleEndTime - handleStartTime)+"ms");
            return;
        }
        long sqlStartTime = System.currentTimeMillis();
        try {
            directlyStoreServiceImpl.handleBigData(directlyStoreInDOs, storeInfo.getCode());
        } catch (Exception e) {
            long sqlEndTime = System.currentTimeMillis();
            this.writeErrorLog(storeInfo.getCode(), ResponseMsg.FAIL_5005.getMsg(), directlyStoreInDOs.size(), (sqlEndTime - sqlStartTime) + "ms");
            e.printStackTrace();
            return;
        }
    }

    private void writeErrorLog(String code, String msg, int size, String time) {
        JSONObject kibanaJson = new JSONObject();
        //日志类型
        kibanaJson.put("logType", "\t\n" +
                "大数据JOB操作日志（直营门店MYSQL）");
        //操作状态：成功/失败
        kibanaJson.put("status", "error");
        //门店编码
        kibanaJson.put("storeCode", code);
        //错误信息
        kibanaJson.put("msg", msg);
        //数据量
        kibanaJson.put("dataSize", size);
        //时长
        kibanaJson.put("timeSize", time);
        log.error(kibanaJson.toString());
    }

    /**
     * 获取按日维度的新增更新数据
     *
     * @param storeInfo
     * @param bigDataGeneralInfoDTOs
     * @param directlyStoreInDOs
     * @return void
     **/
    private void getDayResult(StoreInfoDTO storeInfo, List<BigDataGeneralInfoDTO> bigDataGeneralInfoDTOs, List<DirectlyStoreInDO> directlyStoreInDOs) {
        //直营预测——执行 根据门店和日期获取所有需要更新的数据（默认为13天数据，今日起）
        List<DirectlyStoreInDO> directlyStoreDOs = directlyStoreRepository.listInByStoreAndDate(storeInfo.getCode(), BigDataCacheUtil.dayStartDate, BigDataCacheUtil.dayUpEndDate);
        if (CollectionUtils.isEmpty(directlyStoreDOs)) {
            directlyStoreDOs = new ArrayList<>(1);
        }
        //查询sku信息
        List<SkuInfoDTO> skuInfoDTOS = itemCoreFacade.skuListBySkuCodes(bigDataGeneralInfoDTOs.stream().map(BigDataGeneralInfoDTO::getSkuKey).distinct().collect(Collectors.toList()));
        Map<String, SkuInfoDTO> skuCodeName = this.getSkuInfo(skuInfoDTOS);
        Map<Integer, List<CategorySimpleDTO>> categoryInfo = this.getCategoryInfo(skuInfoDTOS);
        int count;
        List<DirectlyStoreInDO> tmp;
        //新增sku
        String skuKey;
        BaseInDO baseInDO;
        //计数，如果不等于13算无上次预测量，直接新增即可
        for (BigDataGeneralInfoDTO bigDataGeneralInfoDTO : bigDataGeneralInfoDTOs) {
            skuKey = bigDataGeneralInfoDTO.getSkuKey();
            //直营预测————门店
            count = 0;
            tmp = new ArrayList<>();
            for (DirectlyStoreInDO storeDO : directlyStoreDOs) {
                //如果是同一条sku,默认取十三天为更新数据，第十四条为新增数据 存入集合中
                if (skuKey.equals(storeDO.getSkuCode())) {
                    //更新其同用属性（区域，品类）
                    this.setGeneralInfo(storeDO, skuKey, storeInfo, skuCodeName, categoryInfo);
                    this.setStorePredictValue(storeDO, bigDataGeneralInfoDTO);
                    tmp.add(storeDO);
                    count++;
                }
            }
            //如果含有更新数据————加入门店第十四天新增数据   执行所有新增数据,否则新增所有天数据
            baseInDO = this.createGeneralDayInDO(skuKey, storeInfo, skuCodeName, categoryInfo);
            if (count == 13) {
                directlyStoreInDOs.addAll(tmp);
                directlyStoreInDOs.add(this.getNewDirectlyStore(baseInDO, bigDataGeneralInfoDTO.getSalesDayf14()));
            } else {
                //遍历十四天信息
                this.getDayDO(baseInDO, directlyStoreInDOs, bigDataGeneralInfoDTO.getSalesDayf1(), day1);
                this.getDayDO(baseInDO, directlyStoreInDOs, bigDataGeneralInfoDTO.getSalesDayf2(), day2);
                this.getDayDO(baseInDO, directlyStoreInDOs, bigDataGeneralInfoDTO.getSalesDayf3(), day3);
                this.getDayDO(baseInDO, directlyStoreInDOs, bigDataGeneralInfoDTO.getSalesDayf4(), day4);
                this.getDayDO(baseInDO, directlyStoreInDOs, bigDataGeneralInfoDTO.getSalesDayf5(), day5);
                this.getDayDO(baseInDO, directlyStoreInDOs, bigDataGeneralInfoDTO.getSalesDayf6(), day6);
                this.getDayDO(baseInDO, directlyStoreInDOs, bigDataGeneralInfoDTO.getSalesDayf7(), day7);
                this.getDayDO(baseInDO, directlyStoreInDOs, bigDataGeneralInfoDTO.getSalesDayf8(), day8);
                this.getDayDO(baseInDO, directlyStoreInDOs, bigDataGeneralInfoDTO.getSalesDayf9(), day9);
                this.getDayDO(baseInDO, directlyStoreInDOs, bigDataGeneralInfoDTO.getSalesDayf10(), day10);
                this.getDayDO(baseInDO, directlyStoreInDOs, bigDataGeneralInfoDTO.getSalesDayf11(), day11);
                this.getDayDO(baseInDO, directlyStoreInDOs, bigDataGeneralInfoDTO.getSalesDayf12(), day12);
                this.getDayDO(baseInDO, directlyStoreInDOs, bigDataGeneralInfoDTO.getSalesDayf13(), day13);
                this.getDayDO(baseInDO, directlyStoreInDOs, bigDataGeneralInfoDTO.getSalesDayf14(), day14);
            }
        }
    }

    /**
     * 获取直营门店第十四天的新增实体
     *
     * @return {@link DirectlyStoreInDO}
     **/
    private DirectlyStoreInDO getNewDirectlyStore(BaseInDO baseInDO, BigDecimal sales) {
        DirectlyStoreInDO newDirectlyStoreInDO = bigDataBaseConvertor.baseDOToStoreDO(baseInDO);
        newDirectlyStoreInDO.setForecastQty(sales);
        newDirectlyStoreInDO.setConfirmQty(sales);
        newDirectlyStoreInDO.setLastForecastQty(BigDecimal.ZERO);
        newDirectlyStoreInDO.setLastConfirmQty(BigDecimal.ZERO);
        newDirectlyStoreInDO.setFormulateDate(day14);
        newDirectlyStoreInDO.setStatus(0);
        newDirectlyStoreInDO.setUpdateStatus(0);
        return newDirectlyStoreInDO;
    }

    /**
     * 存入门店新增的更新实体
     *
     * @param storeDO
     * @return void
     **/
    private void setStorePredictValue(DirectlyStoreInDO storeDO, BigDataGeneralInfoDTO bigDataGeneralInfoDTO) {
        //获取对应日期的预测值
        Date date = storeDO.getFormulateDate();
        if (day1.equals(date)) {
            this.setDirectStoreValue(storeDO, bigDataGeneralInfoDTO.getSalesDayf1());
            return;
        }
        if (day2.equals(date)) {
            this.setDirectStoreValue(storeDO, bigDataGeneralInfoDTO.getSalesDayf2());
            return;
        }
        if (day3.equals(date)) {
            this.setDirectStoreValue(storeDO, bigDataGeneralInfoDTO.getSalesDayf3());
            return;
        }
        if (day4.equals(date)) {
            this.setDirectStoreValue(storeDO, bigDataGeneralInfoDTO.getSalesDayf4());
            return;
        }
        if (day5.equals(date)) {
            this.setDirectStoreValue(storeDO, bigDataGeneralInfoDTO.getSalesDayf5());
            return;
        }
        if (day6.equals(date)) {
            this.setDirectStoreValue(storeDO, bigDataGeneralInfoDTO.getSalesDayf6());
            return;
        }
        if (day7.equals(date)) {
            this.setDirectStoreValue(storeDO, bigDataGeneralInfoDTO.getSalesDayf7());
            return;
        }
        if (day8.equals(date)) {
            this.setDirectStoreValue(storeDO, bigDataGeneralInfoDTO.getSalesDayf8());
            return;
        }
        if (day9.equals(date)) {
            this.setDirectStoreValue(storeDO, bigDataGeneralInfoDTO.getSalesDayf9());
            return;
        }
        if (day10.equals(date)) {
            this.setDirectStoreValue(storeDO, bigDataGeneralInfoDTO.getSalesDayf10());
            return;
        }
        if (day11.equals(date)) {
            this.setDirectStoreValue(storeDO, bigDataGeneralInfoDTO.getSalesDayf11());
            return;
        }
        if (day12.equals(date)) {
            this.setDirectStoreValue(storeDO, bigDataGeneralInfoDTO.getSalesDayf12());
            return;
        }
        if (day13.equals(date)) {
            this.setDirectStoreValue(storeDO, bigDataGeneralInfoDTO.getSalesDayf13());
        }
    }

    private void setDirectStoreValue(DirectlyStoreInDO storeDO, BigDecimal sales) {
        storeDO.setLastForecastQty(storeDO.getForecastQty());
        storeDO.setForecastQty(sales);
        storeDO.setLastConfirmQty(storeDO.getConfirmQty());
        storeDO.setConfirmQty(sales);
    }

    private void getDayDO(BaseInDO baseInDO, List<DirectlyStoreInDO> directlyStoreInDOs, BigDecimal sales, Date date) {
        DirectlyStoreInDO directlyStoreInDO = bigDataBaseConvertor.baseDOToStoreDO(baseInDO);
        //设置日期
        directlyStoreInDO.setFormulateDate(date);
        //设置预测量
        directlyStoreInDO.setForecastQty(sales);
        directlyStoreInDO.setConfirmQty(sales);
        directlyStoreInDO.setLastConfirmQty(BigDecimal.ZERO);
        directlyStoreInDO.setLastForecastQty(BigDecimal.ZERO);
        directlyStoreInDO.setStatus(0);
        directlyStoreInDO.setUpdateStatus(0);
        directlyStoreInDOs.add(directlyStoreInDO);
    }

    /**
     * 获取（日）通用对象
     *
     * @param skuKey
     * @param storeInfo
     * @param skuCodeName
     * @param categoryInfo
     * @return void
     **/
    private void setGeneralInfo(BaseInDO baseInDO, String skuKey, StoreInfoDTO storeInfo, Map<String, SkuInfoDTO> skuCodeName, Map<Integer, List<CategorySimpleDTO>> categoryInfo) {
        baseInDO.setOpSign(opSign);
        //设置渠道信息
        baseInDO.setChannelCode(BigDataChannelEnum.STORE_DIRECT.getChannelCode());
        baseInDO.setChannelName(BigDataChannelEnum.STORE_DIRECT.getChannelName());
        //设置省市区 门店
        this.setGeneralAreaValue(baseInDO, storeInfo);
        //设置大中小品类
        baseInDO.setSkuCode(skuKey);
        if (skuCodeName.get(skuKey) != null) {
            SkuInfoDTO skuInfoDTO = skuCodeName.get(skuKey);
            baseInDO.setSkuName(skuInfoDTO.getName());
            List<CategorySimpleDTO> categorySimpleDTOS = categoryInfo.get(skuInfoDTO.getCategoryId());
            if (!CollectionUtils.isEmpty(categorySimpleDTOS)) {
                BaseInDO finalDirectlyExecuteInDO = baseInDO;
                categorySimpleDTOS.forEach(subItem -> this.setGeneralCategoryValue(subItem, finalDirectlyExecuteInDO));
            }
        }
    }

    /**
     * 获取（日）通用对象
     *
     * @param skuKey
     * @param storeInfo
     * @param skuCodeName
     * @param categoryInfo
     * @return void
     **/
    private BaseInDO createGeneralDayInDO(String skuKey, StoreInfoDTO storeInfo, Map<String, SkuInfoDTO> skuCodeName, Map<Integer, List<CategorySimpleDTO>> categoryInfo) {
        BaseInDO baseInDO = new BaseInDO();
        baseInDO.setOpSign(opSign);
        //设置渠道信息
        baseInDO.setChannelCode(BigDataChannelEnum.STORE_DIRECT.getChannelCode());
        baseInDO.setChannelName(BigDataChannelEnum.STORE_DIRECT.getChannelName());
        //设置省市区 门店
        this.setGeneralAreaValue(baseInDO, storeInfo);
        //设置大中小品类

        baseInDO.setSkuCode(skuKey);
        if (skuCodeName.get(skuKey) != null) {
            SkuInfoDTO skuInfoDTO = skuCodeName.get(skuKey);
            baseInDO.setSkuName(skuInfoDTO.getName());
            List<CategorySimpleDTO> categorySimpleDTOS = categoryInfo.get(skuInfoDTO.getCategoryId());
            if (!CollectionUtils.isEmpty(categorySimpleDTOS)) {
                BaseInDO finalDirectlyExecuteInDO = baseInDO;
                categorySimpleDTOS.forEach(subItem -> this.setGeneralCategoryValue(subItem, finalDirectlyExecuteInDO));
            }
        }
        return baseInDO;
    }

    /**
     * 设置通用属性 设置品类
     *
     * @param subItem
     * @param baseInDO
     * @return void
     **/
    private void setGeneralCategoryValue(CategorySimpleDTO subItem, BaseInDO baseInDO) {
        Integer level = subItem.getCategoryLevel();
        switch (level) {
            case 2:
                baseInDO.setPriCategoryCode(String.valueOf(subItem.getCategoryCode()));
                baseInDO.setPriCategoryName(subItem.getCategoryName());
                break;
            case 3:
                baseInDO.setSecCategoryCode(String.valueOf(subItem.getCategoryCode()));
                baseInDO.setSecCategoryName(subItem.getCategoryName());
                break;
            case 4:
                baseInDO.setTerCategoryCode(String.valueOf(subItem.getCategoryCode()));
                baseInDO.setTerCategoryName(subItem.getCategoryName());
                break;
            default:
        }
    }

    /**
     * 设置通用属性 设置省市区 门店
     *
     * @param baseInDO
     * @param storeInfo
     * @return void
     **/
    private void setGeneralAreaValue(BaseInDO baseInDO, StoreInfoDTO storeInfo) {
        baseInDO.setProvinceCode(storeInfo.getProvinceCode());
        baseInDO.setProvinceName(storeInfo.getProvinceName());
        baseInDO.setCityCode(storeInfo.getCityCode());
        baseInDO.setCityName(storeInfo.getCityName());
        baseInDO.setCountyCode(storeInfo.getAreaCode());
        baseInDO.setCountyName(storeInfo.getAreaName());
        baseInDO.setStoreCode(storeInfo.getCode());
        baseInDO.setStoreName(storeInfo.getName());
    }

    /**
     * 根据skuInfo获取sku-map信息
     *
     * @param skuInfoDTOS
     * @return void
     **/
    private Map<String, SkuInfoDTO> getSkuInfo(List<SkuInfoDTO> skuInfoDTOS) {
        //根据skuCodes查询skuInfo
        Map<String, SkuInfoDTO> skuCodeName = new HashMap<>();
        if (!CollectionUtils.isEmpty(skuInfoDTOS)) {
            skuCodeName = skuInfoDTOS.stream().collect(Collectors.toMap(SkuInfoDTO::getSkuCode, item -> item));
        }
        return skuCodeName;
    }

    /**
     * 根据skuInfo获取sku品类信息
     *
     * @param skuInfoDTOS
     * @return void
     **/
    private Map<Integer, List<CategorySimpleDTO>> getCategoryInfo(List<SkuInfoDTO> skuInfoDTOS) {
        Map<Integer, List<CategorySimpleDTO>> categoryInfo = new HashMap<>();
        if (!CollectionUtils.isEmpty(skuInfoDTOS)) {
            List<CategorySimpleDTO> categorySimpleDTOS = itemCoreFacade.getAllCategoryByCategoryId(skuInfoDTOS.stream().map(SkuInfoDTO::getCategoryId).distinct().collect(Collectors.toList()));
            categoryInfo = categorySimpleDTOS.stream().collect(Collectors.toMap(CategorySimpleDTO::getId, CategorySimpleDTO::getParentCategoryDTOList));
        }
        return categoryInfo;
    }

    /**
     * 循环获取日补货接口数据
     *
     * @param bigDataGeneralQueryBaseDTO
     * @return void
     **/
    private List<BigDataGeneralInfoDTO> getBigDataGeneralInfo(BigDataGeneralQueryBaseDTO bigDataGeneralQueryBaseDTO) {
        List<BigDataGeneralInfoDTO> itemDTOS = cprfCoreFacade.getDayPredictReplenishment(bigDataGeneralQueryBaseDTO);
        List<BigDataGeneralInfoDTO> bigDataGeneralInfoDTOs = new ArrayList<>(itemDTOS);
        while (itemDTOS.size() == CommonConstants.BIG_DATA_PAGE_SIZE) {
            bigDataGeneralQueryBaseDTO.setPageNo(bigDataGeneralQueryBaseDTO.getPageNo() + 1);
            itemDTOS = cprfCoreFacade.getDayPredictReplenishment(bigDataGeneralQueryBaseDTO);
            bigDataGeneralInfoDTOs.addAll(itemDTOS);
        }
        return bigDataGeneralInfoDTOs;
    }

    /**
     * 大数据查询实体
     *
     * @param storeCode
     * @param nowDateStr
     * @return {@link BigDataGeneralQueryBaseDTO}
     **/
    private BigDataGeneralQueryBaseDTO getGeneralQueryBaseDTO(String storeCode, String nowDateStr) {
        BigDataGeneralQueryBaseDTO bigDataGeneralQueryBaseDTO = new BigDataGeneralQueryBaseDTO();
        bigDataGeneralQueryBaseDTO.setStartDate(nowDateStr);
        bigDataGeneralQueryBaseDTO.setEndDate(nowDateStr);
        bigDataGeneralQueryBaseDTO.setStore(storeCode);
        bigDataGeneralQueryBaseDTO.setPageNo(1);
        bigDataGeneralQueryBaseDTO.setPageSize(CommonConstants.BIG_DATA_PAGE_SIZE);
        return bigDataGeneralQueryBaseDTO;
    }

    /**
     * 插入操作日志
     *
     * @param
     * @return void
     **/
    private void writeOperateLog(Long time) {
        JSONObject kibanaJson = new JSONObject();
        //日志类型
        kibanaJson.put("logType","\t\n" +
                "大数据JOB操作日志（直营门店MYSQL）");
        //操作状态：成功/失败
        kibanaJson.put("status","success");
        //时长
        kibanaJson.put("timeSize",time + "ms");
        log.info(kibanaJson.toString());
    }
}
